从零开始搭建 《vue-hackernews-2.0》

前言

欢迎大家来到我的第一个程序搭建教程: 从零开始搭建 《vue-hackernews-2.0》

项目源码地址: Bulid vue-hackernews-2.0 from Scratch

作为一个前端开发的初学者,在接触学习到 Vue.js 的时候,我发现官方提供并极力推荐的项目《vue-hackernews-2.0》对于新手而言比较难理解。但同时我又被这种高度工业化的项目所吸引,所以我决定花一段时间来弄清楚这个项目到底是怎么运行起来的。 在这个教程中我将会通过利用不同的版本的迭代的方式来逐步重建这个项目。

这个项目是面向初学者的。 整个教程中,我逐步实现了这个项目的四个版本,每一个版本之间有一定的递进关系。为了方便理解掌握每个版本的内容,在每个版本的说明中,我会尽量把在相应版本中所需要掌握的知识点列出来。

注明:简便起见,如未特别说明,在之后的所有章节中我会用 Vue-HN 来代替 vue-hackernews-2.0 项目。 项目运行的系统是 Windows 7,因条件限制未在其他系统上测试,请见谅。

正文

0. Vue.js, Vuex, Vue-router, HN API, Firebase, ES6

章节前言

作为一个前端初学者,我对最原始的页面构造比较熟悉,也就是页面只由 HTML、CSS 以及 JavaScript 构成。所以自从我开始学习 Vue-HN 这个项目我就想是不是可以用最原始的方法来实现这个项目(当然,不考虑用户体验及性能等方面)。在经过多次失败的尝试之后,我终于只利用 Vue.js 及其生态中的一些库实现了这个版本。下面这两个动图就分别是我这个版本和官方版本。


Origin Website
[ 我的版本 ]


Plane Vue.js
[ 官方版本 ]

从上面的动图我们可以清楚的看到,基本上官方给出例子中的所有功能在我这个版本中都被实现出来了,而且仅仅只依赖于 Vue.js 以及其生态中的一些库。在开始搭建这个版本的项目之前,你应该对下面所列的链接中的知识点有所了解。了解完之后开始看代码才会有初步的认识。(了解每个链接对应的知识点即可,链接仅供参考。比如要了解 ES2015 的知识,就很推荐阮一峰老师的教程)

一眨眼看到需要了解这么多资料,你可能会被吓到。其实你不用担心,因为列出来的这些链接中除了 Vue.js 及其生态需要花较多时间来深入了解之外,其与的只需要稍微了解就行。比如 ES6 的内容,你暂时只需要了解箭头函数、 Promise 异步操作、扩展运算符等一些基本知识。另外两个链接则是 API,在用到的时候稍微花一点点时间浏览官方文档就行。

实现过程

搭建这个版本的时候,有几个关键的技术难点需要解决:

  • 怎样在不安装 Firebase 包的情况下通过 HackerNews 提供的 API 获取所需要的数据?
    Answer : 通过阅读 Firebase 的官方文档,我发现可以通过在文件中通过连接引入的方式来获取 Firebase 包的内容。除此之外,本地只需要针对项目稍加配置就行。下面是通过与不通过安装包的两种本地配置代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // without installing firebase package:

    var config = {
    databaseURL: "https://hacker-news.firebaseio.com"
    };
    firebase.initializeApp(config);

    var api = firebase.database().ref('/v0');

    // with firebase package:
    import Firebase from 'firebase'

    const api = inBrowser
    ? new Firebase('https://hacker-news.firebaseio.com/v0')
    : (process.__API__ || (process.__API__ = createServerSideAPI()))

    function createServerSideAPI () {
    const api = new Firebase('https://hacker-news.firebaseio.com/v0')
  • 怎么实现 vuex 和 router 之间的通讯?
    Answer : 保证通讯需要注意以下两点:

    • 在同一个 Vue 实例里面注册这两个插件;
    • 通过一个叫做 “vuex-router-sync” 的插件实现通讯.

      注意: 因为我们这个版本中并没有引入包管理工具,所以我们需要手动将这个插件包的 index.js 文件中的代码提取出来,稍作更改后通过链接的形式引入到主页面*

  • 怎么转换各个单文件组件(也就是 .vue 文件)?
    Answer : 每个单文件组件都包含三个部分: HTML 模板, JavaScript 脚本以及 CSS 样式。模板部分我们通过 “x-template” 来替换, JavaScript 脚本我们通过 “Vue.extend” 来替换, CSS 样式则直接写在样式表中就可以了。如果你想知道详细的信息,那就需要下载源码来瞧瞧了。

在攻克这些难点,又加上对于 Vue.js 及其生态有较深入的了解之后,你应该也能自己完成这个版本的项目搭建过程了!(虽然不能用于生产,没有什么实际意义,但是实现的过程我认为还是很有意思的。)

注意:因为文件之间的依赖原因,所以浏览器对于主页面文件头中的链接加载顺序直接影响到这个版本的成败。经测试,在 Firefox 和 IE 中都会优先加载本地文件然后加载外链,这并不是我所希望的顺序,所以在这两个浏览器中测试失败(可以通过将本地文件上传云存储,然后替换成外链解决)。在 Chrome 中测试成功。

后续版本

目前一切顺利。但是如果我们更耐心些,重新审视这个版本的项目,我们会发现存在一些致命的缺陷。比如所有的文件都很混乱,项目的结构非常的脆弱,而且没有任何措施用于提高访问速度、优化用户体验。这些都会造成很高的维护成本。
在下一个版本中,我们将针对这些作出一些改进。


1. Webpack, Vue.js, Vue-router, Vuex and Hackernews API

章节前言

经过第一个版本后,我们对这个项目有了初步的了解。在这个版本中,我们会使用一些工具,使得我们这个项目初步模块化,同时也更加便于维护。在这个版本中我们将会使用到一些基本的插件和包,并通过 webpack 来实现功能。我们只需要简单的配置一下 webpack 就行,不会涉及到服务器端的内容,不会涉及缓存也不会涉及生产模式及开发模式的不同配置。下面的动图就是这个版本的成品,同时这个动图里面还包含了对整个项目的简单解构。


Basic functions and simple deconstruction
[ 基本功能及简单解构 ]

为了更好的理解这个版本的内容,下面会列出一些需要了解的工具、插件和库的链接,你只需要对这些有基本的了解就行。(如果前面版本中已经列出来了,那么本节将不再重复。比如 Vue.js 及其生态、ES6 等等):

上面的清单好像有点过长,很容易唬到人,不过完全不用担心。因为对于这些工具、插件和库,我们目前所需要了解的仅仅是它们的用途是什么以及怎么正确的使用它们。相信我,这些对于所有人来说都是没有什么难度的。(英语不好的可以借助各种翻译工具,问题也不大)

实现过程

搭建这个版本有几个比较重要的步骤,下面将分别对每一步进行简单的介绍。

第一步

项目结构分析。 其实这一步应该是写在上一个版本中的,因为每当我们开始做一个新的项目的时候,不可避免的需要先去考虑项目的整体结构、功能、风格以及通过怎样是手段来实现这些。分析完以后再来动手代码搭建整个项目才是正确的方式。这个项目的简单解构在上面的动图中已经展示出来了(解构在动图的后半部分,耐心看完)。
简单归纳,项目的结构还是比较简单的,主要由几个不同的页面组成,每个页面又由几个相同或者不同的组件组成。

第二步

获取数据。 把获取数据作为第二步是因为它和项目的整体结构是独立的。在这一步我们需要利用到由 HackerNews 官方提供的 API(这个 API 利用 Firebase 制作)。然后定义一些获取数据的函数,形成 api.js 文件。然后在 Vuex 中调用这些函数,达到数据集中管理的目的。完成这一步后,项目的目录大概是这样子的:

1
2
3
4
| -- src
| | -- store
| | -- api.js
| | -- index.js

第三步

搭建组件及页面。 基于第一步中对于项目的结构分析,我们开始编写组件及页面的代码。先编写组件的代码,然后将组件引入到页面中,再来构建页面。组件直接可以通过 components option 来相互引入,父子组件之间的数据传递可以利用 props 特性。完成这一步之后,项目结构大概是这样子的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| -- src
| | -- store
| | | -- api.js
| | | -- index.js
| |
| | -- components
| | | -- Item.vue
| | | -- ItemList.vue
| | | -- Comment.vue
| | | -- Spinner.vue
| |
| | -- views
| | | -- ItemView.vue
| | | -- UserView.vue
| | | -- CreateListView.js

第四步

完成项目剩下部分。 在这一步中,我们会引入 Vue.js 的路由系统:Vue-router。引入后,我们就可以将组件中的链接替换成路由中的路径。然后利用前文提到过的 vuex-router-sync 插件来同步路由和数据中心。除此之后,我们还需要定义几个全局筛选函数,并且注册在同一个 Vue 实例中。其他还有一些小的工作,比如引入 logo 图片,新建入口文件(为下一步 webpack 打包做准备)等等。完成这一步之后的项目目录大概如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|-- public
| | -- logo-48.png
|
|-- src
| | -- store
| | | -- api.js
| | | -- index.js
| |
| | -- components
| | | -- Item.vue
| | | -- ItemList.vue
| | | -- Comment.vue
| | | -- Spinner.vue
| |
| | -- views
| | | -- ItemView.vue
| | | -- UserView.vue
| | | -- CreateListView.js
| |
| | -- filters
| | | -- index.js
| |
| | -- router
| | |-- index.js
| |
| | -- app.js
| | -- App.vue
| | -- entry.js
| | -- index.html

第五步

终于要用上 webpack 啦! 如果你对于 webpack , node.js 以及 npm 这些内容完全不知道,那么你就需要在开始这一步之前花几个小时的时间来了解一下。 在这个版本中,我们利用包管理工具 npm 来安装或者卸载插件。在初始化我们的项目的时候,我们既可以通过在命令行中输入 npm init (自动新建 node 配置文件,默认文件名为 package.json )又可以自己手动新建一个名为 package.json 的配置文件。安装完各种插件后,我们可以开始配置 webpack 了。为了方便起见,我们会新建一个配置文件,默认文件名为 webpack.config.js ,在这个文件里面我们将会对项目进行简单配置。具体的配置情况可以去看原文件。

注意:在使用 npm 之前,你必须先安装最新版本的 node.js,否则无法运行成功。

后续版本

对比上一个版本,我们在这个版本已经有点取得了一点进步,因为这个版本开始初步模块化了,也更加的易于维护。由于文件依赖及链接加载顺序的的关系,上一个版本只支持在 Chrome 浏览器中运行,而这个版本则不存在这个问题。因为我们在组件化的过程中已将所需要的对应依赖通过模块引入了。
然而,这个版本的项目还远远不够完美。在下一个版本中,我们将会将更多的因素考虑在内,而这些因素都会对用户体验产生很重要的影响。


2. Server, Packages and Plugins for Better Performance

章节前言

于我而言,这个版本是整个项目中最难的一个版本。为了优化用户体验,原作者将很多因素考虑在内,这就会带来很多需要了解的新知识,比如 node.js 中的 express 框架以及各种插件。服务器端及客户端、生产模式及调试模式的不同配置是这个版本的重点。在接下来的片段中,我会将我所理解的部分分享给大家。
由于这个版本和官网的一样,我就不上效果图了。

开发过程

  1. 开启 Node server 之路: server.js
    在一些情况下 你可能会需要在你的项目中使用到 SSR (Server-Side Rendering) ,以此来优化网页。在这个版本中,我们即将这个功能考虑在内。
    既然是服务器端渲染,那么我们首先需要一个服务器。在这个版本中,我们采用的目前非常流行的运行在 Node.js 环境下的 express 框架。通过阅读官方提供的文档,很快我们就可以对此框架有一个初步的了解,知道其主要的用途及使用方法。(中文教程可参考阮一峰老师的 Node.js 入门。)
    Express: Fast, unopinionated, minimalist web framework for Node.js
    除此之外,我们还需要了解一些 Node.js 相关知识,比如 Path 模块、文件系统、系统变量等等。其实对于目前而言,这些内容也只是需要知道它们代表什么意思,以及怎么使用它们就够了。而这也是非常简单的。下面的链接是一些相关的需要了解的内容:
    Node.js v7.7.2 Documentation
    如果你之前不了解 SSR,那么点击下面的链接:
    vue-server-renderer
    除了上面的以外,下面这些插件和包能够使项目性能更加优化:
    serve-favicon: Node.js middleware for serving a favicon
    compression: Node.js compression middleware.
    serialize-javascript: Serialize JavaScript to a superset of JSON
    浏览过上述文档后,我们就具备了理解 server.js 文件的知识储备。这很关键,因为这个文件直通服务器之门。

  2. 生产模式及开发模式
    这两种模式 最大 的区别就是是否具有热替换及热更新功能。在生产模式下, webpack 不会监控文件的更新,在开发模式下是会的。相对于生产模式而言,开发模式更复杂一些(因为会使用到一些中间件),所以我们先来讨论一下开发模式吧。
    正如名字所说,我们只会在开发过程中使用开发模式。所以在这个模式下,我们不是特别关心所加载的文件大小、文件数量以及访问速度等。这些都会导致在两种不同模式下的 webpack 配置文件有部分差异。为了实现热替换及热更新功能,我们可以使用 webpack 提供的 webpack-hot-middlewarewebpack-dev-middleware 中间件。实现热更新的原理是调用 webpack 的 HMR API 来监测服务器变化。相关文档如下:
    webpack-hot-middleware
    除了上面的中间件,还需要了解下面链接中所包含的内容,这会使得我们能更好的的理解开发模式下服务器配置文件,也就是 setup-dev-server.js
    webpack node API
    webpack API: stats-object
    在生产模式下,服务器端会渲染经过 webpack 打包处理过的打包文件,然后客户端接收相关页面。在这个模式下为了提高访问速度,优化用户体验,我们会会使用一些其他的插件。比如下面列出来的两个插件就只是应用在生产模式下:
    extract-text-webpack-plugin
    sw-precache-webpack-plugin
    如果你想知道这两种模式在 webpack 配置上的详细差别,可以去看 webpack.client.config.jswebpack.base.config.js 配置文件。

  3. 服务器端和客户端
    在配置 webpack、获取页面数据的时候,我们既需要配置客户端,也需要配置服务器端,并且要保证它们之间不产生冲突。至于在 Vue.js 中使用 SSR 功能时应该怎样在 webpack 中配置,官方文档有详细说明,可以点击下面的链接:
    vue-server-renderer。保证两端协调工作的一个关键点是在加载初始页面时怎样保证初始数据一致,想了解这点可以去阅读 server-entry.jsclient-entry.js 文件。

  4. 当在命令行中输入 npm run dev 时,具体会发生什么?
    未完待续。

后续版本

基本上这就是我对这个项目的理解了。有一些不懂的地方没有说明,也有一些懂的地方没有说到。之后对整个项目有更深入、更高层次的理解再来补充这一章。在下一个版本中,我将会根据我自己的喜好对整个项目进行一些改造(主要是外在的页面)。下个版本再见。


3. Change the Project as I like

章节前言

在这个版本中,我将会根据自己的喜好对整个网页进行一些改造,使得网页整体更具有 Vue.js 风格,至少从视觉上来说更加的 Vue.js。下面的动图可以预览到我这个版本的成品,为了对照官方例子,你可以去官网查看原网页。对于所添加及更改的部分,本节的下半部分将会详细介绍。


Last Edition of Vue-HackerNews
[ Vue-HackerNews 最后版本 ]

开发过程

  1. 使网页更具 Vue.js 风格
    打开这个版本(或者看上面的动图),一个最明显的变化就是网页的主色调发生了改变。我将页面中所有的橘黄色(#ff6600)都替换成了浅绿色(#41b883)。主色调是一个网站风格的重要组成部分,比如 HackerNews 的橘黄、知乎的深蓝、github 的墨黑以及 Vue.js 的浅绿。
    至少从视觉上,我觉得这更加的 Vue.js 了,嘿嘿。

  2. 增加根据评论数量、时间以及得分排序功能
    排序功能在很多网站上都能见到,能够方便大家快速找到自己想找的内容。所以我认为在这个项目上加上这个功能会比较酷(虽然不确定在这个网页上有没有实际意义)。为了增加这个功能,我新增了如下代码(部分):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // store/index.js
    getters: {
    // Add this function to getters. This function is uesd
    // to change the order of items in activeItems.
    activeItemsSort(state, getters){
    return (m) => {
    return getters.activeItems.sort((A, B) => {
    return A[m] - B[m]
    })
    }
    }

    除此之外在 ItemList.vue 文件中也增加了一些代码,如果你对此感兴趣的话,那么可以把源文件看一下,也挺简单的。 样式上的改动也挺大的,感兴趣也可以去了解下。

  3. 保持在不同设备上的自适应
    为了能和上一个版本一样,仍然能够自适应各种尺寸的设备,我在改变页面布局之后,对相关 CSS 样式进行了设置。从上面的动图可以看到,为了配合排序,主页面的布局发生的变化还是挺大了。在自适应设备的时候,为了使页面看起来更优雅一些,我将部分不太重要的信息给省略了。具体的设置可以看源文件。

  1. ItemView.vue 页面增加 toTop 按钮
    返回顶部按钮也是很多网站的常见功能。由于首页只有20条信息,所以在首页上我没有加上这个。但是在评论页面,动辄上百条,我认为加上这个按钮应该会方便一些。反正都是根据我自己的喜好来加的,有没有实际意义我也不知道,哈哈。

章节结尾

到目前为止,这个项目的所有版本都已经完成了。在完成这些版本的过程中,我感受到了思考和创造带来的乐趣。这就是生活啊,希望大家也能感受到。
最后,衷心感谢原作者 尤雨溪前辈 给我们提供了一个这么好用的框架以及这么精彩的官方示例!

写在最后

从开始接触这个项目到现在已经过去好几个月了,一直想通过学习这个项目来巩固 Vue.js 的知识点,但很长一段时间都没有什么收获。因为知识储备不够,很多内容都看不懂。碰壁碰多了就知道转弯。然后去学习 ES6 以及 Node.js 相关知识,再来看的时候,很多地方就豁然开朗了。在这之后,对这个项目的理解速度就远超过之前了。

一天晚上睡不着,就思考着这种情况对于前端新手应该是挺常见的,我为什么不把自己的经验教训分享出来给大家?然后就有了这个项目。
项目实现的过程遇到各种困难,特别是第一个版本和第三个版本。第一个版本所遇到的关键点在上面已经说过了,不断的阅读各种文档然后尝试,最后终于在 Chrome 中跑了起来。挫败感是常态,成就感是偶尔才有的。第三个版本是官方完整版,自认为直到现在对其的理解程度都只有十之七八。但也暂时不纠结了,没有工作经验,很多在工程中需要考虑的因素对我而言还比较捉摸不透。很多问题在经验丰富的开发者来看是一种常识,但对于新手而言却会感到无比的困惑。“书读百遍,其义自见” 就是这个意思吧。

现在项目完成了,再来回顾整个项目,有点感慨。最开始对这个项目的想法是从简到繁一步一步和大家一起重建这个项目,整个过程会非常详细。现在项目完成了,由于精力和知识层次的限制,却并没有完全达到这种效果,还是有点遗憾。不过除此之外,项目覆盖的还是比较全面的。
最后,希望看过我这个教程的小伙伴能够有所收获;如果有小伙伴或者前辈发现文中出现错误,也请不吝赐教,先谢谢大家了。

赠人玫瑰,手有余香